package org.sunspotworld.demo;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.*;

import com.sun.spot.util.Utils;
import java.util.*;
import java.io.IOException;

public class SensingThread extends      TimerTask
                           implements   IAccelerometer3DThresholdListener,
                                        ITemperatureInputThresholdListener,
                                        ILightSensorThresholdListener
{
    public static final int REFRESH_PERIOD = 250;
    public static final int MIN_TEMP_FARENHEIT = -40;
    public static final int MAX_TEMP_FARENHEIT = 120;
    public static final int TEMP_HIGH_THRESHOLD = 85;
    public static final int TEMP_MAX_THRESHOLD = 100;
    public static final int MIN_LIGHT = 0;
    public static final int MAX_LIGHT = 720;
    public static final double SINGLE_AXIS_THRESHOLD = 1.8;
    public static final double MOTION_MIN_THRESHOLD = 0.1;
    public static final double MOTION_MED_THRESHOLD = 0.6;
    public static final double MOTION_HIGH_THRESHOLD = 1.1;
    public static final double MOTION_MAX_THRESHOLD = 1.5;
    private static final int QUICK_LIGHT_READINGS = 10;

    private double currentTemp;
    private double currentAccel;
    private int currentLight;
    
    private EDemoBoard m_demo;
    private ITriColorLED[] m_leds;
    private ITemperatureInput m_temp;
    private IAccelerometer3D m_accel;
    private ILightSensor m_light;
    
    public SensingThread()
    {
        m_demo = EDemoBoard.getInstance();
        m_leds = m_demo.getLEDs();
        m_temp = m_demo.getADCTemperature();
        m_temp.addITemperatureInputThresholdListener(this);
        m_accel = m_demo.getAccelerometer();
        m_accel.addIAccelerometer3DThresholdListener(this);
        m_light = m_demo.getLightSensor();
        m_light.addILightSensorThresholdListener(this);

        init();
    }
    

    private void init()
    {
        try
        {
            m_accel.setRestOffsets();
            m_accel.setThresholds(m_accel.ALL_AXES, SINGLE_AXIS_THRESHOLD, 0 - SINGLE_AXIS_THRESHOLD, false);
        }
        catch(IOException e)
        {
            System.out.println(e.getMessage());
        }

    }

    /**
     * Sensing loop forever? Senses changes, based on thresholds, and updates
     * AV stuff on the PlanningThread.
     */
    public void run()
    {
        // Use static PlanningThread.localAV

        while(true)
        {
            RefreshEnvironmentReadings();
            PlanningThread.localAV.setArousal(computeLocalArousal());
            PlanningThread.localAV.setValence(computeLocalValence());
            Utils.sleep(REFRESH_PERIOD);
        }
    }

    private void RefreshEnvironmentReadings()
    {
        try
        {
            currentTemp = m_temp.getFahrenheit();
            currentAccel = m_accel.getRelativeAccel();
            currentLight = m_light.getAverageValue(QUICK_LIGHT_READINGS);
            System.out.println("Reading env: " + Double.toString(currentTemp) + "F " +
                    Double.toString(currentAccel) + "G " + Integer.toString(currentLight) + " light");
        }
        catch(IOException e)
        {
            System.out.println(e.getMessage());
        }
    }

    public void thresholdExceeded(IAccelerometer3D accel, int axis, double val, boolean relative) 
    {
        System.out.println("!! ACCEL !! axis: " + Integer.toString(axis) + " val: " + Double.toString(val));
    }

    public void thresholdChanged(IAccelerometer3D accel, int axis, double low, double high, boolean relative) 
    {

    }

    public void thresholdExceeded(ITemperatureInput temp, double val, boolean inCelsius) 
    {
        System.out.println("!! TEMP !!  val: " + Double.toString(val));
    }

    public void thresholdChanged(ITemperatureInput temp, double low, double high, boolean inCelsius) 
    {

    }

    public void thresholdExceeded(ILightSensor light, int val) 
    {
         System.out.println("!! LIGHT !!  val: " + Integer.toString(val));
    }

    public void thresholdChanged(ILightSensor light, int low, int high) 
    {
        
    }


    // Arousal is "energy level" of emotion.  It directly maps to acceleration
    //and temperature.
    private float computeLocalArousal()
    {
        double arousalAcceleration = 0.0;
        double arousalTemperature = 0.0;


        if(currentAccel >= MOTION_MAX_THRESHOLD)
        {
            arousalAcceleration = 1.0;
        }
        else
        {
            arousalAcceleration = currentAccel / MOTION_MAX_THRESHOLD;
        }
        
        arousalTemperature = currentTemp / (MAX_TEMP_FARENHEIT - MIN_TEMP_FARENHEIT);

        //basic average        
        float arousalMod = ((((float)(arousalAcceleration + arousalTemperature)) / 2.0f) - .4f);// / 5.0f;
        
        //change it by a modifier every reading, so it moves towards one end of the spectrum
        return PlanningThread.localAV.getArousal() + arousalMod;
    }

    // Valence is based on Light, primarily...since it is a "positive" emotion
    // At some point, it makes sense that extreme acceleration and/or temperature
    //  could also negatively affect Valence
    private float computeLocalValence()
    {
        double valenceAcceleration = 0.0;
        double valenceTemperature = 0.0;
        double valenceLight = (double)currentLight / (double)(MAX_LIGHT - MIN_LIGHT);

        /*if(currentAccel >= MOTION_HIGH_THRESHOLD)
        {
            valenceAcceleration = 0.2;
        }

        if(currentTemp >= TEMP_MAX_THRESHOLD)
        {
            valenceTemperature = 0.2;
        }*/
        
        return (float) valenceLight;
        //return (float)(((3*valenceLight) - valenceAcceleration - valenceTemperature ) / 3.0);
    }
}
